#include "klib.h"
#include "types.h"
#include <stdarg.h>
#include "log.h"
#include "cpu_instr.h"

void kernel_strcpy(char *dest, const char *src)
{
    if (!dest || !src)
    {
        return;
    }
    while (*src)
    {
        *dest++ = *src++;
    }
    *dest = '\0';
}
void kernel_strncpy(char *dest, const char *src, unsigned int size)
{
    if (!dest || !src || !size)
    {
        return;
    }
    while (*src && size)
    {
        *dest++ = *src++;
        size--;
    }
    if (!size)
    {
        *(dest - 1) = '\0';
    }
    else
    {
        *dest = '\0';
    }
}
int kernel_strlen(const char *str)
{
    if (!str)
    {
        return 0;
    }
    int len = 0;
    while (*str)
    {
        len++;
        str++;
    }
    return len;
}
// int kernel_strcmp(const char *str1,const char *str2);
int kernel_strncmp(const char *str1, const char *str2, unsigned int size)
{
    if (!str1 || !str2 || !size)
    {
        return -1;    // error,not acceptable
    }
    while (*str1 && *str2 && (*str1 == *str2) && size)
    {
        str1++;
        str2++;
        size--;
    }
    if (!size || (!*str1 && !*str2))
        return 0;
    if (*str1 > *str2 || (*str1 && !*str2))
    {
        return 1;
    }
    else
    {
        return -1;
    }
}

void kernel_strcat(char *str1, const char *str2)
{
    if (!str1 || !str2)
    {
        return;
    }
    while (*str1)
    {
        str1++;
    }
    while (*str2)
    {
        *str1++ = *str2++;
    }
    *str1 = '\0';
}
void kernel_strncat(char *str1, const char *str2, unsigned int size)
{
    if (!str1 || !str2)
    {
        return;
    }
    while (*str1)
    {
        str1++;
    }
    while (*str2 && size)
    {
        *str1++ = *str2++;
        size--;
    }
    if (!size)
    {
        *(str1 - 1) = '\0';
    }
    else
    {
        *str1 = '\0';
    }
}

void kernel_memcpy(void *dest, const void *src, unsigned int size)
{
    if (!dest || !src || !size)
    {
        return;
    }
    uint8_t *pdest = (uint8_t *)dest;
    uint8_t *psrc  = (uint8_t *)src;
    while (size)
    {
        *pdest++ = *psrc++;
        size--;
    }
}
void kernel_memset(void *dest, unsigned char val, unsigned int size)
{
    if (!dest || !size)
    {
        return;
    }
    uint8_t *pdest = (uint8_t *)dest;
    while (size)
    {
        *pdest++ = val;
        size--;
    }
}
void kernel_memmove(void *dest, const void *src, unsigned int size)
{
    if (!dest || !src || !size)
    {
        return;
    }
    uint8_t *pdest = (uint8_t *)dest;
    uint8_t *psrc  = (uint8_t *)src;
    if (pdest < psrc || pdest >= psrc + size)
    {
        while (size)
        {
            *pdest++ = *psrc++;
            size--;
        }
    }
    else
    {
        pdest += size - 1;
        psrc += size - 1;
        while (size)
        {
            *pdest-- = *psrc--;
            size--;
        }
    }
}
int kernel_memcmp(const void *str1, const void *str2, unsigned int size)
{
    if (!str1 || !str2 || !size)
    {
        return 1;    // same 1; not same -1
    }
    uint8_t *pstr1 = (uint8_t *)str1;
    uint8_t *pstr2 = (uint8_t *)str2;
    while (size--)
    {
        if (*pstr1++ != *pstr2++)
        {
            return 1;
        }
    }
    return 0;
}
void kernel_vsprintf(char *buff, const char *fmt, va_list args)
{
    enum
    {
        NORMAL,
        READ_FMT
    } state = NORMAL;
    if (!buff || !fmt)
    {
        return;
    }
    char *curr = buff;
    char  ch;
    while ((ch = *fmt++) != '\0')
    {
        switch (state)
        {
        case NORMAL:
            if (ch == '%')
            {
                state = READ_FMT;
            }
            else
            {
                *curr++ = ch;
            }
            break;
        case READ_FMT:
            switch (ch)
            {
            case 'd':
            {
                int num = va_arg(args, int);
                kernel_itoa(curr, num, 10);
                curr += kernel_strlen(curr);
                state = NORMAL;
                break;
            }
            case 'x':
            {
                int num = va_arg(args, int);
                kernel_itoa(curr, num, 16);
                curr += kernel_strlen(curr);
                state = NORMAL;
                break;
            }
            case 'c':
            {
                int chi = va_arg(args, int);
                *curr++ = chi + '\0';
                state   = NORMAL;
                break;
            }
            case 's':
            {
                const char *str = va_arg(args, char *);
                int         len = kernel_strlen(str);
                while (len--)
                {
                    *curr++ = *str++;
                }
                state = NORMAL;
                break;
            }
            default:
                *curr++ = ch;
                state   = NORMAL;
                break;
            }
        }
    }
}
void kernel_sprintf(char *buff, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    kernel_vsprintf(buff, fmt, args);
    va_end(args);
}
void kernel_itoa(char *str, int num0, int base)
{
    static const char *digits = "0123456789ABCDEF";
    char              *curr   = str;
    if (base != 2 && base != 10 && base != 16)
    {
        *curr = '\0';
        return;
    }
    if (num0 < 0 && base == 10)
    {
        *curr++ = '-';
    }
    unsigned int num = (base != 10 || num0 >= 0) ? num0 : -num0;
    char         str_buf[20];
    kernel_memset(str_buf, '\0', sizeof(str_buf));
    int i = 0;
    do
    {
        char ch      = digits[num % base];
        str_buf[i++] = ch;
        /*
        if(tmp>=10){
            str_buf[i++] = tmp-10+'a';
        }else{
            str_buf[i++] = tmp+'0';
        }
        */
        num /= base;
    } while (num);
    if (base == 16)
    {
        str_buf[i++] = 'x';
        str_buf[i++] = '0';
    }
    while (i--)
    {
        *curr++ = str_buf[i];
    }
    *curr = '\0';
}

void panic(const char *file, int line, const char *func, const char *cond)
{
    log_printf("assert failed! %s", cond);
    log_printf("file:%s  line:%d  func:%s", file, line, func);
    for (;;)
    {
        hlt();
    }
}

char *get_file_name(const char *str)
{
    int   len = kernel_strlen(str);
    char *p   = (char *)(str + len);
    while (p > str && *(p - 1) != '/' && *(p - 1) != '\\')
    {
        p--;
    }
    return p;
}
int strings_count(char **argv)
{
    int count = 0;
    if (argv)
    {
        while (*argv++)
        {
            count++;
        }
    }
    return count;
}